﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;

namespace Visual_App
{
    /// <summary>
    /// Hauptform der Visu
    /// </summary>
    public partial class frm_visual : Form
    {

        [DllImport("kernel32.dll")]
        public static extern bool Beep(int Frequenz, int Dauer);

        // Stacks
        public List<IVisual.I_Network> _Stack_N = new List<IVisual.I_Network>();
        public List<Tuple<double, int>> _Stack_E = new List<Tuple<double, int>>();

        public int FEHLER = 0;
        // Stack-threads
        private System.Threading.Thread _Stack_N_Th;
        private System.Threading.Thread _Stack_E_Th;// Veraltet
        // Locks
        private Object _Chart_Gew_Lock = new Object();

        // Steuer-vars
        private int _AUTOCLEAR = -1; // Wenn <0 dann deaktiviert
        private bool _OPT_Uebersicht = true;
        private bool _OPT_WerteG = true;
        private bool _OPT_FehlerG = true;
        private bool _OPT_WerteL = true;
        private bool _OPT_Aktivierungen = true;
        private bool _OPT_SPEC = true;
        private bool _einzel_EPOCH_exec = false;
        private bool _Thread_term = false;
        private string mode = "Logic";

        // ext-visu-data
        private double[,] AP_data = null;
        private double[] TS_data = null;
        private int TS_windowSize = 5;
        private int TS_predictionSize = 1;

        // Ext-Visus
        private _frm_Approx _EXT_Approx;
        private _frm_TimeSeries _EXT_TimeSeries;

        // Loakle Config
        private _XML_Cfg _basic_cfg = new _XML_Cfg();

        // Hidden Pages (veraltet)
        private List<TabPage> _hiddenPages = new List<TabPage>();

        // delegates (integriert in methode)
        private delegate void _del_add_point(int series, double p);
        private delegate void _del_set_image(Image img);
        private delegate void _del_set_2dpoint(int series, double val);
        private delegate void _del_set_3dpoint(double x, double y, double z);
        public delegate void _del_RESET(string mode, params object[] args);
        public delegate void _del_show_3D();
        private delegate void _del_Add_Activation(IVisual.I_Network N);
        private delegate void _del_SET_LV_3D_Data(ListViewItem[] _Data);
        private delegate void _del_SET_PB_3DChart(double porgress);

        // Handler für FS-Redirect
        private System.IO.StreamWriter _FILE = null;

        public int Eingabemuster_anz { get; set; }

        /// <summary>
        /// Worker für den N_thread
        /// </summary>
        private void _Stack_N_Th_Worker()
        {
            int ct = 0;
            while(!this._Thread_term)
            {
                try
                {
                    System.Threading.Thread.Sleep(0);

                    if (this._AUTOCLEAR > 0 && _Stack_N.Count >= this._AUTOCLEAR)
                    {
                        IVisual._SC.cls_LOG.WRITE_LOG_LINE(IVisual._SC.cls_LOG.LOGLEVEL.INFO, "Starte Autoclear...");
                        this._CLEAR_STACK(this._AUTOCLEAR);
                    }

                    // Wenn etwas auf dem stapel liegt
                    if (this._Stack_N.Count > 0)
                    {
                        if (System.Diagnostics.Debugger.IsAttached)
                            System.Diagnostics.Debugger.Break();

                        IVisual.I_Network _tmp = this._Stack_N[0];
                        this._Stack_N.RemoveAt(0);
                        this.draw(_tmp);

                        // jetzt Fehlerstack, wenn alle Muster propagiert wurden und etwas auf dem stack liegt
                        // ersetzt den worker_e_th
                        if (this._Stack_E.Count > 0 && ct % this.Eingabemuster_anz == 0)
                        {
                            Tuple<double, int> _tmp1 = this._Stack_E[0];
                            this._Stack_E.RemoveAt(0);
                            this.update_error(_tmp1.Item1, _tmp1.Item2);

                            ct = 0;
                        }

                        ct++;
                    }
                    else
                    {
                        System.Threading.Thread.Sleep(100); // anderen ausführungszeit geben
                    }
                }catch(Exception ex)
                {
                    IVisual._SC.cls_LOG.WRITE_LOG_LINE(IVisual._SC.cls_LOG.LOGLEVEL.ERROR, ex.Message);
                    this.FEHLER++;
                }
            }
        }

        /// <summary>
        /// Worker für den E_Sack (veraltlet)
        /// </summary>
        private void _Stack_E_Th_Worker()
        {
            while (!this._Thread_term)
            {
                // Wenn etwas auf dem stack liegt...
                if (this._Stack_E.Count > 0)
                {
                    Tuple<double, int> _tmp = this._Stack_E[0];
                    this._Stack_E.RemoveAt(0);
                    this.update_error(_tmp.Item1, _tmp.Item2);
                }
                else
                {
                    System.Threading.Thread.Sleep(100);
                }
            }
        }

        /// <summary>
        /// Setzt das fertig Netzwerk unabhänig vom zeichenfortschritt. (veraltet)
        /// </summary>
        /// <param name="N"></param>
        /// <param name="I"></param>
        /// <param name="O"></param>
        public void SET_FINAL_NETWORK(IVisual.I_Network N, float[][] I, float[][] O)
        {
            IVisual._SC.cls_LOG.WRITE_LOG_LINE(IVisual._SC.cls_LOG.LOGLEVEL.METHOD_INVOKE, "start");

            //this.CALC_DRAW_3D_Data(N, I, O);

            IVisual._SC.cls_LOG.WRITE_LOG_LINE(IVisual._SC.cls_LOG.LOGLEVEL.METHOD_INVOKE, "ende");
        }

        /// <summary>
        /// Zeiche 2D-Punkt. Sowohl für Gewichte als auch ERR
        /// </summary>
        /// <param name="series">Series in die gezichnet werden soll. 0 => Fehlergraph</param>
        /// <param name="val">Wert</param>
        private void set_2dpoint(int series, double val)
        {
            IVisual._SC.cls_LOG.WRITE_LOG_LINE(IVisual._SC.cls_LOG.LOGLEVEL.METHOD_INVOKE, "start");
            // Sperre Chart
            lock (this._Chart_Gew_Lock)
            {
                try
                {
                    if (series > 0)
                    {
                        if (this._OPT_WerteG)
                        {
                            // Zu wenig Series, eine anlegen
                            if (this.chart1.Series.Count <= series - 1)
                            {
                                System.Windows.Forms.DataVisualization.Charting.Series series1 = new System.Windows.Forms.DataVisualization.Charting.Series();
                                series1.ChartArea = "ChartArea1";
                                series1.ChartType = System.Windows.Forms.DataVisualization.Charting.SeriesChartType.Spline;
                                series1.Legend = "Legend1";
                                series1.Name = "Gewicht" + (this.chart1.Series.Count + 1);
                                this.chart1.Series.Add(series1);

                                //C1.Win.C1Chart.ChartDataSeries s = new C1.Win.C1Chart.ChartDataSeries();
                                //s.DataLabel.Text = "Gewicht" + (this.c1Chart1.ChartGroups.Group0.ChartData.SeriesList.Count + 1);
                                //c1Chart1.ChartGroups.Group0.ChartData.SeriesList.Add(s);
                            }

                            //this.c1Chart1.ChartGroups[0].ChartData.SeriesList[series - 1].PointData.Add(new PointF((float)
                            //this.c1Chart1.ChartGroups[0].ChartData.SeriesList[series - 1].PointData.Length, (float)val));
                            // Wert setzten
                            this.chart1.Series[series - 1].Points.AddY(val);
                        }
                    }
                    else
                    {
                        if (this._OPT_FehlerG)
                        {
                            // Fehlergraph
                            this.chart2.Series[0].Points.Add(val);
                        }
                    }

                    if (this._OPT_WerteL)
                    {
                        // Wert auch noch in die dgv eintragen
                        // muss eine spalte geaddet werden?
                        if (this._dgv_werteliste.ColumnCount - 1 <= series)
                        {
                            DataGridViewTextBoxColumn tmp = new DataGridViewTextBoxColumn();
                            tmp.HeaderText = "Gewicht" + (this._dgv_werteliste.ColumnCount - 1);
                            tmp.Name = "Gewicht" + (this._dgv_werteliste.ColumnCount - 1);
                            tmp.ReadOnly = true;
                            this._dgv_werteliste.Columns.Add(tmp);
                        }

                        // Muss eine Zeile geaddet werden?
                        if (this._dgv_werteliste.RowCount == 0 || this._dgv_werteliste.Rows[this._dgv_werteliste.RowCount - 1].Cells[series + 1].Value != null)
                        {
                            this._dgv_werteliste.Rows.Add();
                            this._dgv_werteliste.Rows[this._dgv_werteliste.RowCount - 1].Cells[0].Value = this._dgv_werteliste.RowCount.ToString();
                        }

                        // Wert setzten
                        this._dgv_werteliste.Rows[this._dgv_werteliste.RowCount - 1].Cells[series + 1].Value = val.ToString();
                    }
                }
                catch (Exception ex)
                {
                    IVisual._SC.cls_LOG.WRITE_LOG_LINE(IVisual._SC.cls_LOG.LOGLEVEL.ERROR, ex.Message);
                }
            }

            IVisual._SC.cls_LOG.WRITE_LOG_LINE(IVisual._SC.cls_LOG.LOGLEVEL.METHOD_INVOKE, "ende");
        }

        /// <summary>
        /// Tab visibility ändern (veraltet)
        /// </summary>
        /// <param name="uebersicht"></param>
        /// <param name="err1"></param>
        /// <param name="liste"></param>
        public void change_tab_visibility(bool uebersicht, bool err1, bool liste)
        {
            // Alle einblenden
            for (int i = 0; i < this._hiddenPages.Count; i++)
            {
                if(this._hiddenPages[i].Text == "Übersicht")
                    this._tc_Main.TabPages.Insert(0, this._hiddenPages[i]);
                if (this._hiddenPages[i].Text == "Werte (Graph)")
                    this._tc_Main.TabPages.Insert(1, this._hiddenPages[i]);
                if (this._hiddenPages[i].Text == "Fehler (Graph)")
                    this._tc_Main.TabPages.Insert(2, this._hiddenPages[i]);
                if (this._hiddenPages[i].Text == "Fehler (Fläche)")
                    this._tc_Main.TabPages.Insert(3, this._hiddenPages[i]);
                if (this._hiddenPages[i].Text == "Werte (Liste)")
                    this._tc_Main.TabPages.Insert(4, this._hiddenPages[i]);
            }
            this._hiddenPages.Clear();

            // Jetzt einzelne ausblenden
            if (!uebersicht)
            {
                this._hiddenPages.Add(this._tc_Main.TabPages[0]);
                this._tc_Main.TabPages.RemoveAt(0);
            }

            if (!err1)
            {
                this._hiddenPages.Add(this._tc_Main.TabPages[1]);
                this._tc_Main.TabPages.RemoveAt(1);

                this._hiddenPages.Add(this._tc_Main.TabPages[2]);
                this._tc_Main.TabPages.RemoveAt(2);

                this._hiddenPages.Add(this._tc_Main.TabPages[3]);
                this._tc_Main.TabPages.RemoveAt(3);
            }

            if (!liste)
            {
                this._hiddenPages.Add(this._tc_Main.TabPages[2]);
                this._tc_Main.TabPages.RemoveAt(2);
            }
        }

        /// <summary>
        /// für deleganten
        /// </summary>
        /// <param name="img"></param>
        private void set_image(Image img)
        {
            this.pb_Main.Image = img;
        }

        /// <summary>
        /// resettet alle Parameter
        /// </summary>
        /// <param name="mode"></param>
        /// <param name="args">parameter für ext-visus</param>
        public void RESET(string mode, params object[] args)
        {
            if (this.InvokeRequired)
                this.Invoke(new _del_RESET(this.RESET), mode, args);
            else
            {
                this._tc_Main.SelectedIndex = 0;

                this.mode = mode;
                // EXT-Visus mit daten versorgen
                switch(this.mode)
                {
                    case "Approximation":
                        this.AP_data = (double[,])args[0];
                        break;
                    case "TimeSeries":
                        this.TS_data = (double[])args[0];
                        this.TS_windowSize = (int)args[1];
                        this.TS_predictionSize = (int)args[2];
                        break;
                }

                // Stacks leeren
                this._Stack_E.Clear();
                this._Stack_N.Clear();
                this.FEHLER = 0;
                this._AUTOCLEAR = -1;

                // Charts leeren
                this.chart2.Series[0].Points.Clear();
                this.chart1.Series.Clear();

                //this.c1Chart1.ChartGroups.Group0.ChartData.SeriesList.Clear();

                // DGVs leerem
                this._dgv_aktivirungen.Rows.Clear();
                for (int i = 2; i < this._dgv_aktivirungen.ColumnCount; i++)
                    this._dgv_aktivirungen.Columns.RemoveAt(i);

                this._dgv_werteliste.Rows.Clear();
                for (int i = 2; i < this._dgv_werteliste.ColumnCount; i++)
                    this._dgv_werteliste.Columns.RemoveAt(i);

                // Rechenlog leeren (RTB wegen der länge ;-)
                this._rtb_rechenlog.Text = "";

                // ggf. ext-visus aus vorheriger session closen
                if(this._EXT_Approx != null)
                {
                    this._EXT_Approx.DO_CLOSE();
                    this._EXT_Approx = null;
                }
                if(this._EXT_TimeSeries != null)
                {
                    this._EXT_TimeSeries.DO_CLOSE();
                    this._EXT_TimeSeries = null;
                }

                this.Eingabemuster_anz = 0;

                // Alle Fragen bem benutzer stellen...
                this._OPT_Uebersicht = true;
                this._OPT_WerteG = true;
                this._OPT_FehlerG = true;
                this._OPT_WerteL = true;
                this._OPT_Aktivierungen = true;
                this._OPT_SPEC = true;
                this._ASK_Questions(false);

                // Zusatzvisualierung auch öffnen wenn in OPT deaktiviert, 
                // da sie sonst nicht mehr zugeschaltet werden kann

                // Neue visus instanzieren uns anzeigen
                switch(this.mode)
                {
                    case "Approximation":
                        this._EXT_Approx = new _frm_Approx();
                        this._EXT_Approx.Show();
                        this._EXT_Approx.INIT(this.AP_data);
                        break;
                    case "TimeSeries":
                        this._EXT_TimeSeries = new _frm_TimeSeries();
                        this._EXT_TimeSeries.Show();
                        this._EXT_TimeSeries.windowSize = this.TS_windowSize;
                        this._EXT_TimeSeries.predictionSize = this.TS_predictionSize;
                        this._EXT_TimeSeries.INIT(this.TS_data);
                        break;
                }
            }
        }

        public frm_visual()
        {
            InitializeComponent();
        }

        /// <summary>
        /// trägt den Fehlerwert überall ein (mit delegate)
        /// </summary>
        /// <param name="err"></param>
        /// <param name="Series"></param>
        /// <returns></returns>
        private bool update_error(double err, int Series)
        {
            try
            {
                if (this._FILE != null)
                    this._FILE.WriteLine("E" + Series + "=" + err);

                // Options-IFs in der Methode
                if (this.InvokeRequired)
                    this.Invoke(new _del_set_2dpoint(this.set_2dpoint), Series, err);
                else
                    this.set_2dpoint(Series, err);

                return true;
            }
            catch (Exception) 
            {
                return false;
            }
        }

        /// <summary>
        /// trägt alle werte eines Netzes ein
        /// </summary>
        /// <param name="N"></param>
        /// <returns></returns>
        private bool draw(IVisual.I_Network N)
        {
            try
            {
                // OPT-IF in der Methode
                // Aktivierungen in die dgv eintragen
                Add_Activation(N);

                int ct = 1;
                // für jeden Layer
                for (int l_i = 0; l_i < N.LayersCount; l_i++)
                {
                    // für jedes neuron
                    for (int n_i = 0; n_i < N[l_i].NeuronsCount; n_i++)
                    {
                        // für jeden input
                        for (int i_i = 0; i_i < N[l_i][n_i].InputsCount; i_i++)
                        {
                            // OPT-IF in der Methode
                            set_2dpoint(ct++, N[l_i][n_i][i_i]);

                            if (this._FILE != null)
                                this._FILE.Write("G" + l_i + "/" + n_i + "/" + i_i + "=" + N[l_i][n_i][i_i] + ";");
                        }
                    }
                }

                //this._t_log_refr.Enabled = true;

                if(this._OPT_Uebersicht)
                {
                    Image image;
                    // Höchste Anzahl von Neuronen in einem layer ermitteln..
                    int NC = N[0].InputsCount;
                    for (int i = 0; i < N.LayersCount; i++)
                        if (N[i].NeuronsCount > 0)
                            if (NC < N[i].NeuronsCount)
                                NC = N[i].NeuronsCount;
                    IVisual._SC.cls_LOG.WRITE_LOG_LINE(IVisual._SC.cls_LOG.LOGLEVEL.DEBUG, "NC=" + NC);

                    // Kann die Bildgröße gelassen werden, oder sind scollbars erforderlich?
                    if (this._p_main.Size.Width / NC >= 40)
                        image = new Bitmap(this._p_main.Size.Width, this._p_main.Size.Height);
                    else
                        image = new Bitmap(this._p_main.Size.Width, 40 * NC);

                    using (Graphics g = Graphics.FromImage(image))
                    {
                        IVisual._SC.cls_LOG.WRITE_LOG_LINE(IVisual._SC.cls_LOG.LOGLEVEL.DEBUG, "Zeichnen beginnen");

                        g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.High;
                        g.Clear(Color.White);

                        int pos = image.Width / (N.LayersCount + 1);

                        // Layer (kreise) Zeichen
                        List<List<Point>> positions = new List<List<Point>>();
                        // Eingabelayer zeichen (Layer -1 simulieren)
                        positions.Add(draw_Layer(N[0].InputsCount, g, 0, image.Height, 40, Brushes.Red));
                        // Für alle "echten" Layer
                        for (int LC = 1; LC <= N.LayersCount; LC++)
                        {
                            positions.Add(draw_Layer(N[LC - 1], g, LC * pos, image.Height, 40, Brushes.Red));
                        }
                        IVisual._SC.cls_LOG.WRITE_LOG_LINE(IVisual._SC.cls_LOG.LOGLEVEL.DEBUG, "Layer zeichnen abgeschlossen");

                        // Objekte für Linken bzw. Rechten layer
                        IVisual.I_Layer tmp_NL;
                        IVisual.I_Layer tmp_NR;
                        List<Point> tmp_posL;
                        List<Point> tmp_posR;
                        int mover = 50;

                        IVisual._SC.cls_LOG.WRITE_LOG_LINE(IVisual._SC.cls_LOG.LOGLEVEL.DEBUG, "Verbindungen und Werte zeichnen");
                        // für jeden Layer
                        for (int ctrl = 0; ctrl < N.LayersCount; ctrl++)
                        {
                            int tmp_NL_NeuronsCount = -1;
                            if (ctrl > 0)// ein echter Layr
                            {
                                tmp_NL = N[ctrl - 1];
                                tmp_NR = N[ctrl];

                                tmp_NL_NeuronsCount = tmp_NL.NeuronsCount;
                            }
                            else
                            {
                                // Layer -1:
                                //            WICHTIG ZU WISSEN: N[0].InputsCount != N[0].NeuronsCount!!!!!!
                                tmp_NL = null;// new Layer(N[0].InputsCount);
                                tmp_NR = N[0];

                                tmp_NL_NeuronsCount = N[0].InputsCount;// INPUT NICHT NEURON!!!
                            }

                            // positionen von den draw_layern wiederherstellen
                            tmp_posL = positions[ctrl];
                            tmp_posR = positions[ctrl + 1];

                            // verbimdungen zwischen den Layern zeichnen
                            // für jedes Neuron in Links
                            for (int l_i = 0; l_i < tmp_NL_NeuronsCount; l_i++)
                            {
                                // für jeden neuron in Rechts
                                for (int r_i = 0; r_i < tmp_NR.NeuronsCount; r_i++)
                                {
                                    g.DrawLine(new Pen(Brushes.LightGreen, 3), tmp_posL[l_i], tmp_posR[r_i]);

                                    // Letzer Layer, deshalb die HUD etwas verschieben
                                    if (ctrl == N.LayersCount - 1)
                                    {
                                        mover = 50;
                                        if (r_i == tmp_NR.NeuronsCount - 1)
                                            mover = 30;
                                        if (r_i == 0)
                                            mover = -10;

                                        g.DrawString(get_NeuronInfo(tmp_NR, r_i), new Font("Lucida Console", 8), Brushes.DarkOrange, tmp_posR[r_i].X + 20, tmp_posR[r_i].Y - mover);
                                    }
                                }

                                // HUD Position
                                mover = 50;
                                if (l_i == tmp_NL_NeuronsCount - 1)
                                    mover = 30;
                                if (l_i == 0)
                                    mover = -10;

                                if (ctrl > 0)
                                    g.DrawString(get_NeuronInfo(tmp_NL, l_i), new Font("Lucida Console", 8), Brushes.DarkOrange, tmp_posL[l_i].X + 20, tmp_posL[l_i].Y - mover);
                            }
                        }
                    }

                    IVisual._SC.cls_LOG.WRITE_LOG_LINE(IVisual._SC.cls_LOG.LOGLEVEL.DEBUG, "Verbindungen und Werte zeichnen abgeschlossen");

                    // Bild setzten
                    if (this.InvokeRequired)
                        this.Invoke(new _del_set_image(this.set_image), new object[] { image });
                    else
                        this.set_image(image);
                }

                if (this._OPT_SPEC)
                {
                    // ggf. noch die ext-visus refr
                    IVisual._SC.cls_LOG.WRITE_LOG_LINE(IVisual._SC.cls_LOG.LOGLEVEL.DEBUG, this.mode + " refr");
                    switch (this.mode)
                    {
                        case "Approximation":
                            this._EXT_Approx.REFR(N);
                            break;
                        case "TimeSeries":
                            this._EXT_TimeSeries.REFR(N);
                            break;
                    }
                    IVisual._SC.cls_LOG.WRITE_LOG_LINE(IVisual._SC.cls_LOG.LOGLEVEL.DEBUG, this.mode + " OK");
                }

                // bremse für einzelepoche...
                if (this._einzel_EPOCH_exec)
                    this._einzel_EPOCH_exec = MessageBox.Show("Epoche angeschlossen!\r\nEinel-Epochen-Ausführeung fortsetzten?", "", MessageBoxButtons.YesNo) == System.Windows.Forms.DialogResult.Yes;

                return true;
            }catch(Exception ex){
                IVisual._SC.cls_LOG.WRITE_LOG_LINE(IVisual._SC.cls_LOG.LOGLEVEL.ERROR, ex.Message);
                //MessageBox.Show(ex.Message);
                this.FEHLER++;//Eigentlich IMMER ein RAM Problem :-(
                System.Threading.Thread.Sleep(100);

                return false;
            }
        }

        /// <summary>
        /// Fügt alle aktivierungswerte in die DGV ein
        /// </summary>
        /// <param name="N"></param>
        private void Add_Activation(IVisual.I_Network N)
        {
            if (!this._OPT_Aktivierungen)
                return;

            if (this.InvokeRequired)
            {
                this.Invoke(new _del_Add_Activation(Add_Activation), N);
                return;
            }
            IVisual._SC.cls_LOG.WRITE_LOG_LINE(IVisual._SC.cls_LOG.LOGLEVEL.METHOD_INVOKE, "start");

            int row_ind = row_ind = this._dgv_aktivirungen.Rows.Add();
            int ind = 0;

            // für jeden Layer
            for (int l = 0; l < N.LayersCount; l++)
            {
                // für jedes Neuron
                for (int n = 0; n < N[l].NeuronsCount; n++)
                {
                    //Wenn zu wenig Spalten, eine neue adden
                    if (this._dgv_aktivirungen.ColumnCount - 1 < ind)
                    {
                        DataGridViewTextBoxColumn tmp = new DataGridViewTextBoxColumn();
                        tmp.HeaderText = l + "/" + n;
                        tmp.Name = "A" + l + "" + n;
                        tmp.ReadOnly = true;
                        this._dgv_aktivirungen.Columns.Add(tmp);
                    }
                    
                    // Wert eintragen
                    this._dgv_aktivirungen.Rows[row_ind].Cells[ind].Value = N[l][n].Output;
                
                    if (this._FILE != null)
                        this._FILE.Write("A " + l + "/" + n +"=" + N[l][n].Output + ";");
                    
                    ind++;
                }
            }
            IVisual._SC.cls_LOG.WRITE_LOG_LINE(IVisual._SC.cls_LOG.LOGLEVEL.METHOD_INVOKE, "ende");
        }

        /// <summary>
        /// Neuron analysieren
        /// </summary>
        /// <param name="tmp_NL">Neuron</param>
        /// <param name="l_i">Layerindex</param>
        /// <returns></returns>
        private string get_NeuronInfo(IVisual.I_Layer tmp_NL, int l_i)
        {
            IVisual._SC.cls_LOG.WRITE_LOG_LINE(IVisual._SC.cls_LOG.LOGLEVEL.METHOD_INVOKE, "start");

            StringBuilder bld = new StringBuilder();
            bld.Append("Output: ").Append(tmp_NL[l_i].Output.ToString());//.Append("\n");
            bld.Append(" | Schwellenwert: ").Append(tmp_NL[l_i].Threshold.ToString()).Append("\n");
            bld.Append("Inp Weight Sum: ").Append(tmp_NL[l_i].Sum.ToString()).Append(" | ");
            bld.Append(tmp_NL[l_i].ActivationFunction.NAME()).Append(" Alfa:").Append(tmp_NL[l_i].ActivationFunction.Alfa.ToString()).Append("\n");

            for (int i = 0; i < tmp_NL[l_i].InputsCount; i++)
            {
                bld.Append(tmp_NL[l_i][i]);

                if ((i+1) % 5 == 0)
                    bld.Append("\n");
                else
                    bld.Append(" | ");
            }

            IVisual._SC.cls_LOG.WRITE_LOG_LINE(IVisual._SC.cls_LOG.LOGLEVEL.METHOD_INVOKE, "ende");
            return bld.ToString();
        }

        /// <summary>
        /// Zeichnet einen Layer
        /// </summary>
        /// <param name="layer"></param>
        /// <param name="g">Grphics object</param>
        /// <param name="x">x offset</param>
        /// <param name="height">bildhöhe</param>
        /// <param name="size">größe</param>
        /// <param name="color">Farbe</param>
        /// <returns>Liste mit den Punktkoordinaten</returns>
        private List<Point> draw_Layer(IVisual.I_Layer layer, Graphics g, int x, int height, int size, Brush color)
        {
            return draw_Layer(layer.NeuronsCount, g, x, height, size, color);
        }

        /// <summary>
        /// Zeichnet einen Layer
        /// </summary>
        /// <param name="NeuronsCount">Anzahl der Neuronen in dem Layer</param>
        /// <param name="g">Grphics object</param>
        /// <param name="x">x offset</param>
        /// <param name="height">bildhöhe</param>
        /// <param name="size">größe</param>
        /// <param name="color">Farbe</param>
        /// <returns>Liste mit den Punktkoordinaten</returns>
        private List<Point> draw_Layer(int NeuronsCount, Graphics g, int x, int height, int size, Brush color)
        {
            IVisual._SC.cls_LOG.WRITE_LOG_LINE(IVisual._SC.cls_LOG.LOGLEVEL.METHOD_INVOKE, "start");

            List<Point> ret = new List<Point>();

            // Abstand zwischen den Neuronen ausrechenen
            int dist = (height - size);
            try
            {
                dist = dist / (NeuronsCount - 1);
            }
            catch { }

            // Jedes Neuron zeichen und den punkt in die return Liste adden
            for (int i = 0; i < NeuronsCount; i++)
            {
                g.FillEllipse(color, Convert.ToInt32(x + size / 2), i * dist, size, size);
                ret.Add(new Point(x + size, i * dist + size / 2));
            }

            IVisual._SC.cls_LOG.WRITE_LOG_LINE(IVisual._SC.cls_LOG.LOGLEVEL.METHOD_INVOKE, "ende");
            return ret;
        }

        /// <summary>
        /// Handler für formclosing
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void frm_visual_FormClosing(object sender, FormClosingEventArgs e)
        {
            // Thread beenden und auf das ende warten
            this._Thread_term = true;
            while (this._Stack_N_Th.ThreadState == System.Threading.ThreadState.Running)// || this._Stack_E_Th.ThreadState == System.Threading.ThreadState.Running)
                System.Threading.Thread.Sleep(0);

            //Datendatei schließen, wenn vorhanden
            if (this._FILE != null)
                this._FILE.Close();
            this._FILE = null;

            IVisual._SC.cls_LOG.WRITE_LOG_LINE(IVisual._SC.cls_LOG.LOGLEVEL.DEBUG, "Speichere XML-Config");
            IVisual._SC.cls_Config.Serialize(System.IO.Path.GetDirectoryName(System.Environment.GetEnvironmentVariable("appdata")) + "\\RM_Neuro_VIS_config.xml", _basic_cfg);
            //e.Cancel = !this._allow_close;
        }

        private void frm_visual_Shown(object sender, EventArgs e)
        {
            //this.SendToBack();
        }

        /// <summary>
        /// Einstellungen der Visu abfragen
        /// </summary>
        private void _ASK_Questions(bool IS_RUNNING)
        {
            /*if (MessageBox.Show("Sollen die Daten in eine Datei umgeleitet werden?", "", MessageBoxButtons.YesNo) == System.Windows.Forms.DialogResult.Yes)
            {
                SaveFileDialog sfd = new SaveFileDialog();
                sfd.Filter = "Datendatei|*.csv";

                if (sfd.ShowDialog() == System.Windows.Forms.DialogResult.OK)
                {
                    this._FILE = new System.IO.StreamWriter(sfd.FileName);
                }
            }
            else
            {
                this._FILE = null;
            }

            this._einzel_EPOCH_exec = MessageBox.Show("Einel-Epochen-Ausführeung?", "", MessageBoxButtons.YesNo) == System.Windows.Forms.DialogResult.Yes;

            this._t_log_refr.Enabled = MessageBox.Show("Rechen-Log Anlegen?", "", MessageBoxButtons.YesNo) == System.Windows.Forms.DialogResult.Yes;*/

            _frm_questions _QU = new _frm_questions();
            _QU.VISU_running = IS_RUNNING;
            _QU.REDIR = !(this._FILE == null);
            _QU.PATH = (this._FILE == null) ? "" : "<FESTGELEGT>";
            _QU.EINZELEP = this._einzel_EPOCH_exec;
            _QU.RECHENL = this._t_log_refr.Enabled;
            _QU.Uebersicht = this._OPT_Uebersicht;
            _QU.WerteG = this._OPT_WerteG;
            _QU.FehlerG = this._OPT_FehlerG;
            _QU.WerteL = this._OPT_WerteL;
            _QU.Aktivierungen = this._OPT_Aktivierungen;
            _QU.SPEC = this._OPT_SPEC;

            if(_QU.ShowDialog() == System.Windows.Forms.DialogResult.OK)
            {
                if(!IS_RUNNING)
                {
                    if (_QU.REDIR)
                        this._FILE = new System.IO.StreamWriter(_QU.PATH);
                    else
                        this._FILE = null;
                }

                this._einzel_EPOCH_exec = _QU.EINZELEP;
                this._t_log_refr.Enabled = _QU.RECHENL;
                this._OPT_Uebersicht = _QU.Uebersicht;
                this._OPT_WerteG = _QU.WerteG;
                this._OPT_FehlerG = _QU.FehlerG;
                this._OPT_WerteL = _QU.WerteL;
                this._OPT_Aktivierungen = _QU.Aktivierungen;
                this._OPT_SPEC = _QU.SPEC;
            }
        }

        /// <summary>
        /// Ruft das RechenLog ab
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void _t_log_refr_Tick(object sender, EventArgs e)
        {
            this._rtb_rechenlog.AppendText(cls_Remote_Interf.Rechen_log());
        }

        /*
        void selectNext(System.Windows.Forms.ContextMenu cm)
        {
            int sel = 0;
            for (int i = 0; i < cm.MenuItems.Count; i++)
            {
                if (cm.MenuItems[i].Checked)
                {
                    sel = i;
                    break;
                }
            }

            if (sel >= cm.MenuItems.Count - 1)
                sel = 0;
            else
                sel++;

            if (cm.MenuItems[sel].Text.Equals("-"))
            {
                if (sel >= cm.MenuItems.Count - 1)
                    sel = 0;
                else
                    sel++;
            }

            cm.MenuItems[sel].PerformClick();
        }
        */

        private void frm_visual_Load(object sender, EventArgs e)
        {
            //this._tc_Main.TabPages.RemoveAt(3);

            // Lokale Config
            IVisual._SC.cls_LOG.WRITE_LOG_LINE(IVisual._SC.cls_LOG.LOGLEVEL.DEBUG, "Lade XML-Config");
            if (System.IO.File.Exists(System.IO.Path.GetDirectoryName(System.Environment.GetEnvironmentVariable("appdata")) + "\\RM_Neuro_VIS_config.xml"))
            {
                try
                {
                    _basic_cfg = (_XML_Cfg)IVisual._SC.cls_Config.Deserialize(System.IO.Path.GetDirectoryName(System.Environment.GetEnvironmentVariable("appdata")) + "\\RM_Neuro_VIS_config.xml", typeof(_XML_Cfg));
                    IVisual._SC.cls_LOG.LOG_LEVEL = _basic_cfg.LOGLEVEL;
                    IVisual._SC.cls_LOG.HTML = _basic_cfg.HTML;
                }
                catch (Exception ex)
                {
                    IVisual._SC.cls_LOG.WRITE_LOG_LINE(IVisual._SC.cls_LOG.LOGLEVEL.ERROR, ex.Message);
                }
            }

            IVisual._SC.cls_LOG.WRITE_LOG_LINE(IVisual._SC.cls_LOG.LOGLEVEL.INFO, "Starte AD Server");
            //AutoDiscovery-Server
            System.ComponentModel.BackgroundWorker workerAutoDiscovery = new System.ComponentModel.BackgroundWorker();
            workerAutoDiscovery.WorkerReportsProgress = true;
            workerAutoDiscovery.WorkerSupportsCancellation = true;
            IVisual._SC.AutoDiscovery.cls_AutoDiscovery_Server svcAutoDiscovery = new IVisual._SC.AutoDiscovery.cls_AutoDiscovery_Server(ref workerAutoDiscovery, IVisual._SC.cls_tools.LocalIPAddress(), _basic_cfg.VI_Port);
            svcAutoDiscovery.AutoDiscoveryPort++;
            svcAutoDiscovery.packetBytes = new byte[] { 0x2, 0x3, 0x4 };
            workerAutoDiscovery.DoWork += new System.ComponentModel.DoWorkEventHandler(svcAutoDiscovery.Start);
            //workerAutoDiscovery.ProgressChanged += new System.ComponentModel.ProgressChangedEventHandler(logWorkers_ProgressChanged);
            workerAutoDiscovery.RunWorkerAsync();

            IVisual._SC.cls_LOG.WRITE_LOG_LINE(IVisual._SC.cls_LOG.LOGLEVEL.DEBUG, "Starte Remoting");
            //Remoteing
            System.Runtime.Remoting.Channels.BinaryServerFormatterSinkProvider serverSinkProvider = new System.Runtime.Remoting.Channels.BinaryServerFormatterSinkProvider();
            serverSinkProvider.TypeFilterLevel = System.Runtime.Serialization.Formatters.TypeFilterLevel.Full;

            IVisual._SC.cls_LOG.WRITE_LOG_LINE(IVisual._SC.cls_LOG.LOGLEVEL.DEBUG, "Starte Remoting für Config");
            // Remoting für Config
            System.Runtime.Remoting.Channels.Tcp.TcpServerChannel srv_RM_Neuro_Visual;
            srv_RM_Neuro_Visual = new System.Runtime.Remoting.Channels.Tcp.TcpServerChannel("srv_RM_Neuro_Config", _basic_cfg.VI_Port, serverSinkProvider);
            System.Runtime.Remoting.Channels.ChannelServices.RegisterChannel(srv_RM_Neuro_Visual, false);
            System.Runtime.Remoting.WellKnownServiceTypeEntry remObj = new System.Runtime.Remoting.WellKnownServiceTypeEntry
            (
                typeof(cls_Remote_Interf),
                "_RM_Neuro_Visual",
                System.Runtime.Remoting.WellKnownObjectMode.Singleton
            );
            System.Runtime.Remoting.RemotingConfiguration.RegisterWellKnownServiceType(remObj);

            IVisual._SC.cls_LOG.WRITE_LOG_LINE(IVisual._SC.cls_LOG.LOGLEVEL.DEBUG, "Starte Stack Threads");
            //Worker Threads
            this._Stack_N_Th = new System.Threading.Thread(this._Stack_N_Th_Worker);
            this._Stack_N_Th.Start();
            // veraltet
            this._Stack_E_Th = new System.Threading.Thread(this._Stack_E_Th_Worker);
            //this._Stack_E_Th.Start();
        }

        /// <summary>
        /// Monitor der stacks
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void _t_lokmon_refr_Tick(object sender, EventArgs e)
        {
            this._l_lokmon_stacksize_N.Text = "N-Stack: " + this._Stack_N.Count;
            this._l_lokmon_stacksize_E.Text = "E-Stack: " + this._Stack_E.Count;
            this._l_lokmon_epoche_E.Text = "Epoche: " + this.chart2.Series[0].Points.Count;
            if (this.chart1.Series.Count > 0)
                this._l_lokmon_epoche_N.Text = "Epoche: " + this.chart1.Series[0].Points.Count;

            if(this.FEHLER > 0)
                this._l_lokmon_Fehlercount.BackColor = Color.Red;
            else
                this._l_lokmon_Fehlercount.BackColor = Color.Transparent;
            this._l_lokmon_Fehlercount.Text = "Fehler: " + this.FEHLER;

            if (this.FEHLER > 10 && this.FEHLER < 20)
                Beep(1000, 500);
        }

        private void _cmd_optionen_Click(object sender, EventArgs e)
        {
            this._ASK_Questions(this._Stack_E.Count > 0 || this._Stack_N.Count > 0);
        }

        private void _cmd_skip_Click(object sender, EventArgs e)
        {
            _frm_skip _SK = new _frm_skip();
            _SK._SKIP_EPO = this._AUTOCLEAR < 0 ? (int)(this._Stack_N.Count * 0.8) : this._AUTOCLEAR;
            _SK._AutoClear = this._AUTOCLEAR >= 0;
            if(_SK.ShowDialog() == System.Windows.Forms.DialogResult.OK)
            {
                if(_SK._AutoClear)
                {
                    this._AUTOCLEAR = _SK._SKIP_EPO;
                    return;
                }
                this._AUTOCLEAR = -1;

                if(_CLEAR_STACK(_SK._SKIP_EPO))
                    MessageBox.Show("Erfolgeich!");
                else
                    MessageBox.Show("Fehlergeschlagen!\r\nDie Visualisierung ist nicht mehr konsistent.");
            }
        }

        private bool _CLEAR_STACK(int EPOs)
        {
            IVisual._SC.cls_LOG.WRITE_LOG_LINE(IVisual._SC.cls_LOG.LOGLEVEL.METHOD_INVOKE, "start");

            try
            {
                IVisual._SC.cls_LOG.WRITE_LOG_LINE(IVisual._SC.cls_LOG.LOGLEVEL.DEBUG, "Starte das löschen von " + EPOs + " Epochen...");
                if (EPOs >= this._Stack_N.Count)
                {
                    this._Stack_N.Clear();
                    this._Stack_E.Clear();
                }
                else
                {
                    for (int i = 1; i <= EPOs; i++)
                    {
                        if (this._Stack_E.Count == 0)
                            break;

                        this._Stack_N.RemoveAt(0);

                        // jetzt Fehlerstack, wenn alle Muster propagiert wurden und etwas auf dem stack liegt
                        // ersetzt den worker_e_th
                        if (this._Stack_E.Count > 0 && i % this.Eingabemuster_anz == 0)
                            this._Stack_E.RemoveAt(0);
                    }
                }
                IVisual._SC.cls_LOG.WRITE_LOG_LINE(IVisual._SC.cls_LOG.LOGLEVEL.DEBUG, "Löschen der Epochen beendet.");
                IVisual._SC.cls_LOG.WRITE_LOG_LINE(IVisual._SC.cls_LOG.LOGLEVEL.METHOD_INVOKE, "ende");
                return true;
            }
            catch (Exception ex)
            {
                IVisual._SC.cls_LOG.WRITE_LOG_LINE(IVisual._SC.cls_LOG.LOGLEVEL.ERROR, ex.Message);
                IVisual._SC.cls_LOG.WRITE_LOG_LINE(IVisual._SC.cls_LOG.LOGLEVEL.METHOD_INVOKE, "ende");
                return false;
            }
        }
    }
}
